﻿SmitearWebGL.Core.Algorithm.PerlinNoise = {};

SmitearWebGL.Core.Algorithm.PerlinNoise.noise2d = function(x, y)
{
	var p = SmitearWebGL.Core.Algorithm.PerlinNoise._noiseData;
	var fade = SmitearWebGL.Core.Algorithm.PerlinNoise.fade;
	var lerp = SmitearWebGL.Core.Algorithm.PerlinNoise.lerp;
	var grad = SmitearWebGL.Core.Algorithm.PerlinNoise.grad;

	var X = Math.floor(x) & 255;
	var Y = Math.floor(y) & 255;

	x -= Math.floor(x);
	y -= Math.floor(y);

	var u = fade(x);
	var v = fade(y);

	var A = p[X  ]+Y;
	var AA = p[A];
	var AB = p[A+1];

	var B = p[X+1]+Y;
	var BA = p[B];
	var BB = p[B+1];

	return lerp(v, lerp(u, grad(p[AA], x  , y  , 0),
			grad(p[BA], x-1, y  , 0)),
			lerp(u, grad(p[AB], x  , y-1, 0),
			grad(p[BB], x-1, y-1, 0)));
}

SmitearWebGL.Core.Algorithm.PerlinNoise.noise3d = function(x, y, z)
{
	var p = SmitearWebGL.Core.Algorithm.PerlinNoise._noiseData;
	var fade = SmitearWebGL.Core.Algorithm.PerlinNoise.fade;
	var lerp = SmitearWebGL.Core.Algorithm.PerlinNoise.lerp;
	var grad = SmitearWebGL.Core.Algorithm.PerlinNoise.grad;

	var X = Math.floor(x) & 255;
	var Y = Math.floor(y) & 255;
	var Z = Math.floor(z) & 255;

	x -= Math.floor(x);
	y -= Math.floor(y);
	z -= Math.floor(z);

	var u = fade(x);
	var v = fade(y);
	var w = fade(z);

	var A = p[X]+Y;
	var AA = p[A]+Z;
	var AB = p[A+1]+Z;

	var B = p[X+1]+Y;
	var BA = p[B]+Z;
	var BB = p[B+1]+Z;

	return lerp(w, lerp(v, lerp(u, grad(p[AA  ], x  , y  , z   ),
		grad(p[BA], x-1, y  , z   )),
		lerp(u, grad(p[AB  ], x  , y-1, z   ),
		grad(p[BB], x-1, y-1, z   ))),
		lerp(v, lerp(u, grad(p[AA+1], x  , y  , z-1 ),
		grad(p[BA+1], x-1, y  , z-1 )),
		lerp(u, grad(p[AB+1], x  , y-1, z-1 ),
		grad(p[BB+1], x-1, y-1, z-1 ))));
}

SmitearWebGL.Core.Algorithm.PerlinNoise.fade = function(t) { return t * t * t * (t * (t * 6 - 15) + 10); }

SmitearWebGL.Core.Algorithm.PerlinNoise.lerp = function(t, a, b) { return a + t * (b - a); }

SmitearWebGL.Core.Algorithm.PerlinNoise.grad = function(hash, x, y, z)
{
	var h = hash & 15;
	var u = h<8 ? x : y;
	var v = h<4 ? y : h==12||h==14 ? x : z;
	return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}

SmitearWebGL.Core.Algorithm.PerlinNoise.fBm2d = function(x, y, octaves, gain)
{
	var lacunarity = 2.0;

	var freq = 1.0;
	var amp = 0.5;

	var sum = 0;

	octaves = octaves || 8;
	gain = gain || 0.5;

	for(var i=0; i<octaves; i++)
	{
		sum += SmitearWebGL.Core.Algorithm.PerlinNoise.noise2d(x*freq, y*freq)*amp;
		freq *= lacunarity;
		amp *= gain;
	}
	return sum;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.fBm3d = function(x, y, z, octaves, gain)
{
	var lacunarity = 2.0;

	var freq = 1.0;
	var amp = 0.5;

	var sum = 0;

	octaves = octaves || 8;
	gain = gain || 0.5;

	for(var i=0; i<octaves; i++)
	{
		sum += SmitearWebGL.Core.Algorithm.PerlinNoise.noise3d(x*freq, y*freq, z*freq)*amp;
		freq *= lacunarity;
		amp *= gain;
	}
	return sum;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.turbulence2d = function(x, y, octaves, gain)
{
	var lacunarity = 2.0;

	var sum = 0;
	var freq = 1.0;
	var amp = 1.0;

	octaves = octaves || 8;
	gain = gain || 0.5;

	for(var i=0; i<octaves; i++)
	{
		sum += Math.abs(SmitearWebGL.Core.Algorithm.PerlinNoise.noise2d(x*freq, y*freq))*amp;
		freq *= lacunarity;
		amp *= gain;
	}
	return sum;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.turbulence3d = function(x, y, z, octaves, gain)
{
	var lacunarity = 2.0;

	var sum = 0;
	var freq = 1.0;
	var amp = 1.0;

	octaves = octaves || 8;
	gain = gain || 0.5;

	for(var i=0; i<octaves; i++)
	{
		sum += Math.abs(SmitearWebGL.Core.Algorithm.PerlinNoise.noise3d(x*freq, y*freq, z*freq))*amp;
		freq *= lacunarity;
		amp *= gain;
	}
	return sum;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.ridge = function(h, offset)
{
	h = Math.abs(h);
	h = offset - h;
	h = h * h;
	return h;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.ridgedmf2d = function(x, y, octaves, gain)
{
	var lacunarity = 2.0;
	var offset = 1.0;

	var sum = 0;
	var freq = 1.0;
	var amp = 0.5;
	var prev = 1.0;

	octaves = octaves || 8;
	gain = gain || 0.5;

	for(var i=0; i<octaves; i++)
	{
		var n = SmitearWebGL.Core.Algorithm.PerlinNoise.ridge(SmitearWebGL.Core.Algorithm.PerlinNoise.noise2d(x*freq, y*freq), offset);
		sum += n*amp*prev;
		prev = n;
		freq *= lacunarity;
		amp *= gain;
	}
	return sum;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.ridgedmf3d = function(x, y, z, octaves, gain)
{
	var lacunarity = 2.0;
	var offset = 1.0;

	var sum = 0;
	var freq = 1.0;
	var amp = 0.5;
	var prev = 1.0;

	octaves = octaves || 8;
	gain = gain || 0.5;

	for(var i=0; i<octaves; i++)
	{
		var n = SmitearWebGL.Core.Algorithm.PerlinNoise.ridge(SmitearWebGL.Core.Algorithm.PerlinNoise.noise3d(x*freq, y*freq, z*freq), offset);
		sum += n*amp*prev;
		prev = n;
		freq *= lacunarity;
		amp *= gain;
	}
	return sum;
}

SmitearWebGL.Core.Algorithm.PerlinNoise.generate = function( pData, width, height, octaves, gain, type )
{
	type = type == undefined ? SmitearWebGL.Core.Algorithm.PerlinNoise.Type.FBM : type;
	
	var noiseFunc = null;
	
	switch ( type )
	{
		case SmitearWebGL.Core.Algorithm.PerlinNoise.Type.FBM :
			noiseFunc = SmitearWebGL.Core.Algorithm.PerlinNoise.fBm2d;
			break;
			
		case SmitearWebGL.Core.Algorithm.PerlinNoise.Type.NOISE :
			noiseFunc = SmitearWebGL.Core.Algorithm.PerlinNoise.noise2d;
			break;
			
		case SmitearWebGL.Core.Algorithm.PerlinNoise.Type.TURBLUENCE :
			noiseFunc = SmitearWebGL.Core.Algorithm.PerlinNoise.turbulence2d;
			break;
			
		case SmitearWebGL.Core.Algorithm.PerlinNoise.Type.RIDGEDMF :
			noiseFunc = SmitearWebGL.Core.Algorithm.PerlinNoise.ridgedmf2d;
			break;
		
		default :
			noiseFunc = SmitearWebGL.Core.Algorithm.PerlinNoise.fBm2d;
			break;
	}
	
	var j = 0;

	for (var x = 0; x < width; x++)
	{
		for (var y = 0; y < height; y++)
		{
			var result = noiseFunc( x / width * 10, y / height * 10, octaves, gain );
			result = Math.floor( result * 127.5 + 127.5 );

			pData[j + 0] = result;
			pData[j + 1] = result;
			pData[j + 2] = result;
			pData[j + 3] = 255;

			j += 4;
		}
	}
}

SmitearWebGL.Core.Algorithm.PerlinNoise._noiseData = new Array( 256 );
for ( var j = 0; j < 256; ++ j )
SmitearWebGL.Core.Algorithm.PerlinNoise._noiseData[j] = Math.floor( Math.random() * 255 );

SmitearWebGL.Core.Algorithm.PerlinNoise.Type =
{
	FBM :			0,
	NOISE :			1,
	TURBLUENCE :	2,
	RIDGEDMF : 		3
}